package com.tvunetworks.center.device.config;

import com.tvunetworks.center.device.service.feign.TvuSearchFeignService;
import feign.Request;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import feign.Contract;
import feign.Feign;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.http.MediaType;
import org.springframework.context.annotation.Bean;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author kevin
 * 动态配置feign服务名
 */
@Component
public class RemoteFeignClient {


    @Value("${searchFeignName:tvu-search}")
    private String searchFeignName;


    /**
     * FeignClientFactoryBean 该工厂类中 设置builder属性时就是通过该对象，源码中可看到
     */
    protected FeignContext feignContext;

    /**
     * 通过注入Eureka实例对象，就不用手动指定url，只需要指定服务名即可
     */
    protected EurekaClient eurekaClient;

    /**
     *
     */
    private static final Map<String, Object> FEIGN_CLIENTS = new ConcurrentHashMap<>();

    /**
     *
     * @param eurekaClient eurekaClient
     * @param feignContext feignContext
     */
    @Autowired
    public void init(final EurekaClient eurekaClient, final FeignContext feignContext) {
        this.eurekaClient = eurekaClient;
        this.feignContext = feignContext;
    }

    /**
     *
     * @return a
     */
    @Bean
    public TvuSearchFeignService getQcloudService() {
        return create(TvuSearchFeignService.class, searchFeignName);
    }


    /**
     * 设置编码解码器为FastJson
     *
     * @param clazz a
     * @param serverId a
     * @param <T> a
     * @return a
     */
    protected <T> T create(Class<T> clazz, String serverId) {
        InstanceInfo nextServerFromEureka = eurekaClient.getNextServerFromEureka(serverId, false);
        String key = nextServerFromEureka.getIPAddr() + ":" + nextServerFromEureka.getPort() + ":" + clazz.getName();
        Object object = FEIGN_CLIENTS.get(key);
        if (Objects.isNull(object)) {
            object = Feign.builder()
                    //encoder指定对象编码方式
                    .encoder(this.feignEncoder())
                    //decoder指定对象解码方式
                    .decoder(this.feignDecoder())
                    //.client(feignClient)
                    //options方法指定连接超时时长及响应超时时长
                    .options(new Request.Options(5000, 5000))
                    //retryer方法指定重试策略
                    //.retryer(new Retryer.Default(5000, 5000, 3))
                    .contract(feignContext.getInstance(serverId, Contract.class))
                    //target方法绑定接口与服务端地址。返回类型为绑定的接口类型。
                    .target(clazz, nextServerFromEureka.getHomePageUrl());
            FEIGN_CLIENTS.put(key, object);
        }

        return (T) object;
    }

    /**
     *
     * @return a
     */
    protected Encoder feignEncoder() {
        return new SpringEncoder(feignHttpMessageConverter());
    }

    /**
     *
     * @return a
     */
    protected Decoder feignDecoder() {
        return new SpringDecoder(feignHttpMessageConverter());
    }

    /**
     *
     * @return a
     */
    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
        return () -> httpMessageConverters;
    }

    /**
     *
     * @return a
     */
    private FastJsonHttpMessageConverter getFastJsonConverter() {
        FastJsonHttpMessageConverter converter =
                new FastJsonHttpMessageConverter();

        List<MediaType> supportedMediaTypes = new ArrayList<>();
        MediaType mediaTypeJson =
                MediaType.valueOf(MediaType.APPLICATION_JSON_UTF8_VALUE);
        supportedMediaTypes.add(mediaTypeJson);
        converter.setSupportedMediaTypes(supportedMediaTypes);
        FastJsonConfig config = new FastJsonConfig();
        config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
        config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
        converter.setFastJsonConfig(config);

        return converter;
    }
}
